﻿using System;

using RayDen.Library.Core;
using RayDen.Library.Core.Primitives;

namespace RayDen.MegaRay.Imaging
{
    using vec4 = Float4;

    public class GaussianSpectrumConverter
    {
        const float White = 1.0f;

        static vec4 Cyan0 = new vec4(0.0, 0.424537460743542, 0.0866503554583976, 0.560757618949125);
        static vec4 Cyan1 = new vec4(0.0, 0.246400896854156, 0.0795161416808855, 0.216116362841135);
        static vec4 Cyan2 = new vec4(1.0, 0.067666394964209, 0.2698588575757230, 0.890716186803857);
        
        static vec4 Magenta0 = new  vec4(0.0, 0.092393363155047, -0.030670840714796, 0.425200104381996);
        static vec4 Magenta1 = new  vec4(0.0, 0.174734179228986, 0.0690508593874629, 0.983929883263911);
        static vec4 Magenta2 = new  vec4(1.0, 0.613995338323662, 0.0794711389383399, 1.003105061865860);
        
        static vec4 Yellow0 =new  vec4(0.0, 0.369673263739623, -0.071355497310236, 0.503666150930812);
        static vec4 Yellow1 =new  vec4(0.0, 0.558410218684172,  0.151858057162275, 0.878349029651678);
        static vec4 Yellow2 =new  vec4(1.0, 0.587945864428471,  0.101005427723483, 0.109960421083442);
        
        static vec4 Red0 =new  vec4(0.0, 0.574803873802654,  0.0349961565910619, 0.670478585641923);
        static vec4 Red1 =new  vec4(0.0, 0.042753652345675, -0.076576978780864,  0.070884754752968);
        static vec4 Red2 =new  vec4(1.0, 0.669048230499984,  0.0587027396330119, 0.957999219817480);
        
        static vec4 Green0 =new  vec4(0.0, 0.305242141596798,  0.0337596436768638, 0.424248514020785);
        static vec4 Green1 =new  vec4(0.0, 0.476992126451749, -0.0541085157876399, 0.815789194891182);
        static vec4 Green2 =new  vec4(1.0, 0.365833471799225, -0.0583175076362409, 0.792406519710127);
        
        static vec4 Blue0 =new  vec4(0.0, 0.144760614900738, 0.0848347582999023, 0.993361426917213);
        static vec4 Blue1 =new  vec4(0.0, 0.600421286424602, -0.060880809655396, 0.0744873773945442);
        static vec4 Blue2 =new  vec4(1.0, 0.231505955455338, -0.029894351908322, 0.339396172335299);



        static float Gaussian( float x0,  float s,  float w,  float x)
        {
          return w * MathLab.Exp( -(x - x0) * (x - x0) / (2.0f * s * s + 1.0e-20f));
        }



        static float GaussianMixture( float lambda, ref vec4 Data0, ref vec4 Data1, ref vec4 Data2)
        {
            float t = (lambda - 0.380f) / (0.780f - 0.380f);
            float g0 = Gaussian(Data0.y, Data0.z, Data0.w, t);
            float g1 = Gaussian(Data1.y, Data1.z, Data1.w, t);
            float g2 = Gaussian(Data2.y, Data2.z, Data2.w, t);

            return Math.Min(Math.Max(g0 + g1 + g2 + Data0.x, Data1.x), Data2.x);
        }



        public float RGB2Spectrum(ref RgbSpectrum rgb, float lambda)
        {
            float r2g = rgb.c1 - rgb.c2;
            float g2b = rgb.c2 - rgb.c3;
            float b2r = rgb.c3 - rgb.c1;

            if ((rgb.c1 <= rgb.c2) && (rgb.c1 <= rgb.c3)) 
            {
                if (rgb.c2 <= rgb.c3)
                {
                    return rgb.c1 * White - (r2g * GaussianMixture(lambda, ref Cyan0, ref Cyan1, ref Cyan2) + g2b * GaussianMixture(lambda, ref Blue0, ref Blue1, ref Blue2)); 
                }
                else
                {
                    return rgb.c1 * White + (b2r * GaussianMixture(lambda, ref Cyan0, ref Cyan1, ref Cyan2) + g2b * GaussianMixture(lambda, ref Green0, ref Green1, ref Green2));
                }
            }
            else if ((rgb.c2 <= rgb.c1) && (rgb.c2 <= rgb.c3)) 
            {
                if (rgb.c3 <= rgb.c1)
                {
                    return rgb.c2 * White - (g2b * GaussianMixture(lambda, ref Magenta0, ref Magenta1, ref Magenta2) + b2r * GaussianMixture(lambda, ref Red0, ref Red1, ref  Red2)); 
                }
                else
                {
                    return rgb.c2 * White + (r2g * GaussianMixture(lambda, ref Magenta0, ref Magenta1, ref Magenta2) + b2r * GaussianMixture(lambda, ref Blue0, ref Blue1, ref Blue2));
                }
            }
            else 
            {
                if (rgb.c1 <= rgb.c2)
                {
                    return rgb.c3 * White - (b2r * GaussianMixture(lambda, ref Yellow0, ref Yellow1, ref Yellow2) + r2g * GaussianMixture(lambda, ref Green0, ref Green1, ref Green2));
                }
                else
                {
                    return rgb.c3 * White + (g2b * GaussianMixture(lambda, ref Yellow0, ref Yellow1, ref Yellow2) + r2g *GaussianMixture(lambda, ref Red0, ref Red1, ref Red2));
                }
            }
        }

   }
}
